package gc;

/**
 * @author eii
 * @time 2020/12/23 9:05 下午
 */
public class BetterSetJvm {

/*
6.JVM在什么情况下会加载一个类
答 :
a.JVM进程启动之后，代码中包含"main()"方法的主类一定会被加载到内存。
b.执行"main()"方法代码的过程中，遇到别的类也会从对应的".class"字节码文件加载 对应的类到内存里面。

7.一个类从加载到使用，一般会经历哪些过程

答：加载->验证->准备->解析->初始化->使用->卸载

a.加载:将编译好的".class"字节码文件加载到JVM中
b.验证:根据JVM规范，校验加载进来的".class"字节码文件
c.准备:给类和类变量分配一定的内存空间，且给类变量设置默认的初始值(0或者nul)
d.解析:把符号引用替换为直接引用的过程
e.初始化:根据类初始化代码给类变量赋值

注：执行new函数来实例化类对象会触发类加载到初始化的全过程；或者是包含"main()"方 法的主类，必须是立马初始化的。如果初始化一个类的时候，发现他的父类还没初始化， 那么必须先初始化他的父类。

8.Java里有哪些类加载器
答:
a.启动类加载器: 主要负责加载我们在机器上安装的Java目录(lib目录)下的核心类库
b.扩展类加载器：主要负责加载Java目录下的"lib/ext"目录中得类
C.应用程序类加载器：主要负责加载"ClassPath"环境变量所指定的路径中的类，大致 可以理解为加载我们写好的java代码
d.自定义类加载器：根据自己的需求加载类

9.什么是双亲委派机制

答：
JVM的类加载器是有亲子层级结构的，启动类加载器最上层，扩展类加载器第二层， 应用程序类加载器第三层，自定义类加载器第四层。
当应用程序类加载器需要加载一个类时， 他会先委派给自己的父类加载器去加载，最终传导到顶层的类加载器去加载，
但是如果父类加载器在自己负责加载的范围内，没找到这个类，那么就会下推加载权利给自己的子类加载器
 */
/*
总结:
rabbitmq丢失数据的情况：
1、生产者端。消息因为网络问题丢失或者发送到rabbitmq时出错了。
2、rabbitmq服务端。未做持久化。
3、消费者端。打开了autoAck，在未完成消费之前就自动回复了。

rabbitmq丢失数据的解决：
1、生产者端。
通过confirm模式异步确认消息发送成功，在失败后的回调函数中处理失败的逻辑。
2、服务端。
打开持久化机制。这里涉及到两个参数，一个是建立queue的时候，持久化那个queue。
另外一个是生产者发送消息的时候，把deliveryMode设置为2，让MQ把这条数据也给持久化。
但是尽管如此，如果在极端情况下，在rabbitmq中内存写成功，但是还没来及持久化时，rabbitmq宕机，这部分在内存里面的数据也会丢失，不过几率很小。

3、在消费者端，去掉autoAck，在自己完成逻辑后手动提交ack。

====================================================

总 结 ：
kafka出现数据丢失的情况：
1、生产者端：和rabbitmq类似，如果没能确认写成功，也没有重发那么也会丢失。
2、服务端：如果未来得及和从节点同步数据就宕机了，那么这部分数据就会丢失。
3、消费者端：和rabbitmq类似，如果自动提交offset依旧会出现丢失。

kafka出现数据丢失的解决：

1、生产者端：设置参数，要求每个从节点都写成功后才任务成功，另外如果发送失败，重试次数设置一个很大的值。
2、服务端：设置参数，要求从节点起码大于1，且至少有一个能被感知到。
3、消费者端：取消掉自动回复。 不过，强一致的保证消息不丢失，必然会影响到吞吐量。
 */


    /*
    jvm优化

    整体设置，根据机器和业务模型，对新生代和老年代选择合适对垃圾收集器
    目前项目使用的是：-XX:+UseParNewGC -XX:+UseConcMarkSweepGC

    而g1是以垃圾回收优先的回收器，主要是通过将内存区域划分成region然后维护一个回收价值列表，
    建立一个可预测的时间停顿模型。内部保留了年代，但是仅仅是逻辑上进行了保留，新生代和老年代会根据需要进行变化。

问：后市面上都用G1清理，那我们是不是在jvm优化上就得靠边站了？
回答：
是的，使用G1的时候，其实能做的事情很少，因为你想，他所有的内存分配和GC时机都是动态变化的，
你怎么去调优？实际上他一切都是自动运行的。只要他能保证每次GC的耗时在你指定范围就可以了。
但是其实现在G1也未必就已经很稳定了，所以一般还是用CMS+ParNew就可以了，比较可控一些。

    新生代设置
    1、设置 1、NewSize 2、MaxNewSize 3、SurvivorRatio
    根据
    - 新生代占用空间的增长速率： 计算合适MaxNewSize，减少younggc频率
    - 新生代垃圾回收后的存活对象的大小： 计算合适SurvivorRatio，从而分配合适的Survivor大小，避免younggc后存活对象因为Survivor过小，或者达到50%的动态年龄判定的阈值，从而进入老年代
    合理设置 1、NewSize 2、MaxNewSize 的大小，以及 3、SurvivorRatio 的比例

    2、设置 MaxTenuringThreshold
    根据业务特性，合理设置MaxTenuringThreshold，让长久存活对象在合适的年龄进入老年代。短期存活的对象留在新生代

    3、设置 PretenureSizeThreshol
    大对象主要指字符串和数组,虚拟机提供了 -XX:PretenureSizeThreshold参数,大于这个值的参数直接在老年代分配

    老年代设置
    1、合理设置老年代大小，避免因老年代过小，导致 历次younggc后存活平均大小 > 老年代空间 条件的成立，频繁进行full gc
    通过堆大小MaxHeapSize，减去新生代大小MaxNewSize，得到老年代大小
    例如 -XX:NewSize=209715200 -XX:MaxNewSize=209715200 -XX:InitialHeapSize=314572800 -XX:MaxHeapSize=314572800

    2、设置 1、UseCMSCompactAtFullCollection  2、CMSFullGCsBeforeCompaction
    针对CMS内存碎片问题，可设置 Full GC过后就整理一次内存碎片
    原因：
    因为碎片整理不及时，会导致随着一次一次Full GC导致老年代产生更多的内存碎片，连续可用内存越来越少，
    进而本来新生代晋升上来本可存下的对象，因没有可用连续空间，触发FUll GC。
    恶性循环触发FUll GC的速度就会越快。
    如下设置，每次full gc都执行内存碎片整理
    -XX:+UseCMSCompactAtFullCollection
    -XX:CMSFullGCsBeforeCompaction=0

    3、另外两个参数优化FUll GC
    CMSParallelInitialMarkEnabled：开启后会在CMS垃圾回收器的“初始标记”阶段开启多线程并发执行
    CMSScavengeBeforeRemark：开启后会在CMS的重新标记阶段之前，先尽量执行一次Young GC
    作用：
    CMS的重新标记也是会Stop the World的，所以所以如果在重新标记之前，先执行一次Young GC，就会回收掉一些年轻代里没有人引用的对象
    所以如果先提前回收掉一些对象，那么在CMS的重新标记阶段就可以少扫描一些对象，此时就可以提升CMS的重新标记阶段的性能， 减少耗时


    4、待优化模版，针对内存8g的机器，给jvm 堆内存分配4g
    -Xms4096M -Xmx4096M -Xmn3072M -Xss1M -XX:PermSize=256M -XX:MaxPermSize=256M
    -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=92 -XX:+UseCMSInitiatingOccupancyOnly
    -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0
    -XX:+CMSParallelRemarkEnabled -XX:+CMSScavengeBeforeRemark

    5、JVM在OOM的时候自动dump内存快照，
    JVM的启动参数中加入如下的一些参数：
    -XX:+HeapDumpOnOutOfMemoryError
    -XX:HeapDumpPath=/usr/local/app/oom

    6、禁止代码中显示调用GC
    DisableExplicitGC,这个参数作用是禁止代码中显示调用GC。代码如何显示调用GC呢,通过System.gc()函数调用
    ** 需要注意的时，像jetty 使用java nio 会使用DirectByteBuffer对象，申请堆外内存，为了避免堆外内存溢出
    他使用System.gc()，辅助回收DirectByteBuffer对象，所以此时不需要DisableExplicitGC
    **DisableExplicitGC 这个参数的使用，具体情况具体分析


    7、总结JVM优化模板，针对内存8g的机器，给jvm 堆内存分配4g
-Xms4096M -Xmx4096M -Xmn3072M -Xss1M -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M
-XX:+UseParNewGC -XX:+UseConcMarkSweepGC -XX:CMSInitiatingOccupancyFraction=92 -XX:+UseCMSInitiatingOccupancyOnly
-XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction=0 -XX:+CMSParallelInitialMarkEnabled -XX:+CMSScavengeBeforeRemark
-XX:+DisableExplicitGC -XX:+PrintGCDetails -Xloggc:gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/usr/local/app/oom

    */
 /*
fullgc的发生情况：
1.老年代可用内存小于新生代全部对象的大小，又没有开启空间担保，就会直接触发fullgc。
2.如果新生代存活大小大于老年代空间，并且老年代空间小于历次晋升的平均内存大小，也会执行fullgc。
3.大对象或者动态年龄进入老年代，而老年代空间不足，也会执行fullgc。
4.如果是cms回收器，那么老年代内存使用到92%之后，就会触发fullgc，因为并发清除阶段需要给用户线程预留内存空间。

频繁Full GC的几种常见原因
1、Survivor区域过小，导致对象频繁进入老年代，频繁触发Full GC
2、代码逻辑不合理，每次列表查询从数据库加载大量数据，导致对象过大，直接进入老年代，提前full gc，因修改代码逻辑，大列表数据分批查询和处理
3、Metaspace（永久代）过小，导致加载类过多触发Full GC
4、代码中不合理使用System.gc()，触发了Full GC
5、代码bug，死循环导致对象无限创建，最终oom
6、硬件性能和配置过低，导致无法执行大数据和并发任务，应多机集群部署和升级配置
7、不合理使用动态代理，生成过多动态类，导致Metaspace满，而oom
8、tomcat处理一个请求耗时过长，导致请求堆积，频繁full gc，甚至oom
例如一个请求处理需要4s，qps为100，也就是在第4秒时，此时工作线程有400个在同时运行，
线程引用的对象不能释放，这样占用堆空间过大，不仅会频繁full gc，甚至oom
解决：1、优化代码逻辑，提升处理速度  2、设置超时时间为1s，这样能减少同时线程数



针对第七点，CGlib的Enhancer针对某个类动态生成了一个子类，这个子类你完全可以缓存起来
不要每次都动态生成，生成一次后用全部变量缓存起来，以后直接使用这个全局变量，避免生成动态类过多，导致Metaspace满




总结一下 jvm的参数设置：
-XX:+CMSParallelInitialMarkEnabled表示在初始标记的多线程执行，减少STW；
-XX:+CMSScavengeBeforeRemark：在重新标记之前执行minorGC减少重新标记时间；
-XX:+CMSParallelRemarkEnabled:在重新标记的时候多线程执行，降低STW；
-XX：CMSInitiatingOccupancyFraction=92和-XX:+UseCMSInitiatingOccupancyOnly配套使用，如果不设置后者，jvm第一次会采用92%但是后续jvm会根据运行时采集的数据来进行GC周期，如果设置后者则jvm每次都会在92%的时候进行gc；
-XX:+PrintHeapAtGC:在每次GC前都要GC堆的概况输出



老师好，线上也出现了FUll GC的告警，日志如下:

2019-09-05T17:26:15.161+0800: 85779.869: [Full GC (Metadata GC Threshold) 2019-09-05T17:26:15.161+0800: 85779.869: [CMS: 472256K->445559K(3022848K), 2.0333919 secs] 1425388K->445559K(4910336K),
 [Metaspace: 277217K->277217K(1511424K)], 2.0355295 secs] [Times: user=2.01 sys=0.01, real=2.03 secs] 2019-09-05T17:26:17.197+0800: 85781.905:
 [Full GC (Last ditch collection) 2019-09-05T17:26:17.197+0800: 85781.905: [CMS: 445559K-
>382990K(3022848K), 1.6770458 secs] 445559K->382990K(4910336K), [Metaspace: 276037K->276037K(1511424K)], 1.6863552 secs] [Times: user=1.63 sys=0.01, real=1.68 secs] 2019-09-05T17:26:18.886+0800: 85783.594:
[GC (CMS InitialMark) [1 CMS-initial-mark: 382990K(3022848K)] 382992K(4910336K), 0.0134842 secs] [Times: user=0.02 sys=0.00, real=0.02 secs]

初步确认是每次发布groovy脚本，classload的时候才会彪高，这种问题基本定位到了，但是需要怎么去优化和解决呢?

回答：

这个很明确了，你的metaspace太小了，所以classload太多的时候会触发full gc，只要给metaspace区域更大空间就可以了
*/

}
